package com.ziven.learn.view

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.animation.PathInterpolator
import android.widget.Button
import android.widget.Scroller
import android.widget.TextView
import com.ziven.learn.R

private const val X_ZERO = 140F
private const val Y_ZERO = 1000F
private const val LENGTH = 800F
private const val ARROW = 16F
private const val AXIS_LENGTH = 750F
private const val POINT_LENGTH = 48.0F

private const val FIRST_LENGTH1_X = 100F
private const val FIRST_LENGTH1_Y = 600F
private const val FIRST_LENGTH2_X = 100F
private const val FIRST_LENGTH2_Y = 600F

private const val SCROLL_TIME = 5000
private const val SCROLL_START_X = X_ZERO
private const val SCROLL_START_Y = Y_ZERO + 400F
private const val SCROLL_END_X = X_ZERO + LENGTH
private const val SCROLL_END_Y = SCROLL_START_Y

class CustomDrawView : View {
    private val axisView: TextView by lazy {
        val tv: TextView = (parent as ViewGroup).findViewById(R.id.axis_show)
        tv.apply { setTextColor(Color.RED) }
    }
    private val axisTry: Button by lazy {
        (parent as ViewGroup).findViewById(R.id.axis_try)
    }
    //坐标轴
    private val axisPath = Path()
    private val axisPaint = Paint()

    //曲线
    private val path = Path()
    private val paint = Paint()
    private val finalPath = Path()

    //两个坐标运动区域
    private val pointRect = RectF(
        X_ZERO,
        Y_ZERO - AXIS_LENGTH,
        X_ZERO + AXIS_LENGTH,
        Y_ZERO,
    )
    //两个坐标当前的实际坐标系中的位置
    private val pointPosition = RectF(
        X_ZERO + FIRST_LENGTH1_X,
        Y_ZERO - FIRST_LENGTH1_Y,
        X_ZERO + AXIS_LENGTH - FIRST_LENGTH2_X,
        Y_ZERO - AXIS_LENGTH + FIRST_LENGTH2_Y,
    )
    //两个坐标当前的位置在定义坐标系中对应的0到1位置
    private val point0To1 = RectF(
        String.format("%.2f", FIRST_LENGTH1_X / AXIS_LENGTH).toFloat(),
        String.format("%.2f", FIRST_LENGTH1_Y / AXIS_LENGTH).toFloat(),
        String.format("%.2f", (AXIS_LENGTH - FIRST_LENGTH2_X) / AXIS_LENGTH).toFloat(),
        String.format("%.2f", (AXIS_LENGTH - FIRST_LENGTH2_Y) / AXIS_LENGTH).toFloat(),
    )
    //第一个坐标绘制的区域
    private val point1= RectF(
        pointPosition.left - POINT_LENGTH,
        pointPosition.top - POINT_LENGTH,
        pointPosition.left + POINT_LENGTH,
        pointPosition.top + POINT_LENGTH,
    )
    //第二个坐标绘制的区域
    private val point2 = RectF(
        pointPosition.right - POINT_LENGTH,
        pointPosition.bottom - POINT_LENGTH,
        pointPosition.right + POINT_LENGTH,
        pointPosition.bottom + POINT_LENGTH,
    )
    private val pointPaint = Paint()
    private var movePoint1 = false
    private var movePoint2 = false

    private var mScroller: Scroller? = null
    private val scrollerPath = Path().apply {
        moveTo(SCROLL_START_X, SCROLL_START_Y)
        lineTo(SCROLL_END_X, SCROLL_END_Y)
    }
    private val scrollerRect = RectF(
        SCROLL_START_X - POINT_LENGTH,
        SCROLL_START_Y - POINT_LENGTH,
        SCROLL_START_X + POINT_LENGTH,
        SCROLL_START_Y + POINT_LENGTH,
    )
    private var scrollX = SCROLL_START_X.toInt()
    private var scrollY = SCROLL_START_Y.toInt()

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    init {
        axisPaint.color = Color.RED
        axisPaint.style = Paint.Style.STROKE
        axisPaint.strokeWidth = 5F

        pointPaint.color = Color.RED
        pointPaint.style = Paint.Style.FILL

        paint.color = Color.BLUE
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 3F

        setAxisPath()
    }

    private fun getText(): String = "(${point0To1.left}, ${point0To1.top}, ${point0To1.right}, ${point0To1.bottom})"

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        axisView.text = getText()
        axisTry.setOnClickListener {
            mScroller = Scroller(context, PathInterpolator(finalPath))
            mScroller?.startScroll(
                SCROLL_START_X.toInt(),
                SCROLL_START_Y.toInt(),
                LENGTH.toInt(),
                0,
                SCROLL_TIME)
            postInvalidate()
        }
        drawPath(point0To1.left, point0To1.top, point0To1.right, point0To1.bottom)
    }

    override fun computeScroll() {
        super.computeScroll()
        mScroller ?: return
        if (mScroller!!.computeScrollOffset()) {
            scrollX = mScroller!!.currX
            scrollY = mScroller!!.currY
            scrollerRect.left = scrollX - POINT_LENGTH
            scrollerRect.top = scrollY - POINT_LENGTH
            scrollerRect.right = scrollX + POINT_LENGTH
            scrollerRect.bottom = scrollY + POINT_LENGTH
            postInvalidate()
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        val x = event.x
        val y = event.y

        if (event.action == MotionEvent.ACTION_DOWN) {
            movePoint1 = point1.contains(x, y)
            movePoint2 = if (movePoint1) false else point2.contains(x, y)
        }

        if (!movePoint1 && !movePoint2) return true
        if (!pointRect.contains(x, y)) return true

        if (movePoint1) {
            point1.set(event.x - POINT_LENGTH, event.y - POINT_LENGTH, event.x + POINT_LENGTH, event.y + POINT_LENGTH)
            pointPosition.left = event.x - X_ZERO
            pointPosition.top = Y_ZERO - event.y
            point0To1.left = String.format("%.2f", pointPosition.left / AXIS_LENGTH).toFloat()
            point0To1.top = String.format("%.2f", pointPosition.top / AXIS_LENGTH).toFloat()
        } else if (movePoint2) {
            point2.set(event.x - POINT_LENGTH, event.y - POINT_LENGTH, event.x + POINT_LENGTH, event.y + POINT_LENGTH)
            pointPosition.right = event.x - X_ZERO
            pointPosition.bottom = Y_ZERO - event.y
            point0To1.right = String.format("%.2f", pointPosition.right / AXIS_LENGTH).toFloat()
            point0To1.bottom = String.format("%.2f", pointPosition.bottom / AXIS_LENGTH).toFloat()
        }

        axisView.text = getText()
        drawPath(point0To1.left, point0To1.top, point0To1.right, point0To1.bottom)
        return true
    }

    private fun drawPath(x1: Float, y1: Float, x2: Float, y2: Float) {
        finalPath.reset()
        finalPath.moveTo(0.0F, 0.0F)
        path.reset()
        path.moveTo(X_ZERO, Y_ZERO)
        for(i in 0..AXIS_LENGTH.toInt()) {
            val xy = toXY(i / AXIS_LENGTH, x1, y1, x2, y2)
            finalPath.lineTo(xy[0], xy[1])
            path.lineTo(X_ZERO + xy[0] * AXIS_LENGTH, Y_ZERO - xy[1] * AXIS_LENGTH)
        }
        postInvalidate()
    }

    private fun toXY(t: Float, x1: Float, y1: Float, x2: Float, y2: Float): FloatArray {
        return FloatArray(2).apply {
            this[0] = to(t, 0.0F, x1, x2, 1.0F)
            this[1] = to(t, 0.0F, y1, y2, 1.0F)
        }
    }

    private fun to(t: Float, p0: Float, p1: Float, p2: Float, p3: Float): Float {
        val r = (1 - t) * (1 - t) * (1 - t) * p0 + 3.0 * (1 - t) * (1 - t) * t * p1 + 3.0 * (1 - t) * t * t * p2 + t * t * t * p3
        return r.toFloat()
    }

    override fun onDraw(canvas: Canvas) {
        canvas.drawRect(point1, pointPaint)
        canvas.drawRect(point2, pointPaint)

        canvas.drawPath(scrollerPath, axisPaint)
        canvas.drawRect(scrollerRect, pointPaint)

        canvas.drawPath(axisPath, axisPaint)

        canvas.drawPath(path, paint)
    }

    private fun setAxisPath() {
        axisPath.reset()
        axisPath.moveTo(X_ZERO, Y_ZERO)
        axisPath.lineTo(X_ZERO + LENGTH, Y_ZERO)

        axisPath.moveTo(X_ZERO + LENGTH - ARROW, Y_ZERO - ARROW)
        axisPath.lineTo(X_ZERO + LENGTH, Y_ZERO)
        axisPath.lineTo(X_ZERO + LENGTH - ARROW, Y_ZERO + ARROW)

        axisPath.moveTo(X_ZERO, Y_ZERO)
        axisPath.lineTo(X_ZERO, Y_ZERO - LENGTH)

        axisPath.moveTo(X_ZERO - ARROW, Y_ZERO - LENGTH + ARROW)
        axisPath.lineTo(X_ZERO, Y_ZERO - LENGTH)
        axisPath.lineTo(X_ZERO + ARROW, Y_ZERO - LENGTH + ARROW)

        axisPath.moveTo(X_ZERO, Y_ZERO - AXIS_LENGTH)
        axisPath.lineTo(X_ZERO + AXIS_LENGTH, Y_ZERO - AXIS_LENGTH)
        axisPath.lineTo(X_ZERO + AXIS_LENGTH, Y_ZERO)
    }
}